﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;

namespace WJP.ObjectModel
{
    /// <summary>
    /// 一个实现了属性变更通知的对象
    /// </summary>
    [DataContract]
    [Serializable]
    public class ObservableObject : Extendable, INotifyPropertyChanging, INotifyPropertyChanged
    {
        [NonSerialized]
        private bool _suppressPropertyChange;

        [NonSerialized]
        private PropertyChangedEventHandler _PropertyChangedChangedHandlers;

        [NonSerialized]
        private PropertyChangingEventHandler _PropertyChangingChangedHandlers;

        /// <summary>
        /// WCF 序列化使用。
        /// </summary>
        [DataMember]
        private Dictionary<string, object> Properties { get; set; } = new Dictionary<string, object>();

        /// <summary>
        /// 属性变更后事件
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged
        {
            add
            {
                this._PropertyChangedChangedHandlers = this._PropertyChangedChangedHandlers + value;
            }
            remove
            {
                this._PropertyChangedChangedHandlers = this._PropertyChangedChangedHandlers - value;
            }
        }

        /// <summary>
        /// 属性变更前事件
        /// </summary>
        public event PropertyChangingEventHandler PropertyChanging
        {
            add
            {
                this._PropertyChangingChangedHandlers = this._PropertyChangingChangedHandlers + value;
            }
            remove
            {
                this._PropertyChangingChangedHandlers = this._PropertyChangingChangedHandlers - value;
            }
        }

        /// <summary>
        /// 属性变更前事件
        /// </summary>
        /// <param name="propertyName"></param>
        protected virtual void OnPropertyChanging(string propertyName)
        {
            if (!_suppressPropertyChange || _PropertyChangingChangedHandlers == null)
                return;
            _PropertyChangingChangedHandlers(this, new PropertyChangingEventArgs(propertyName));
        }

        /// <summary>
        /// 属性变更后事件
        /// </summary>
        /// <param name="propertyName"></param>
        protected virtual void OnPropertyChanged(string propertyName)
        {
            if (!_suppressPropertyChange || _PropertyChangedChangedHandlers == null)
                return;
            _PropertyChangedChangedHandlers(this, new PropertyChangedEventArgs(propertyName));
        }

        /// <summary>
        /// 设置为不支持属性变更通知
        /// </summary>
        public void ResumeNotifyPropertyChange()
        {
            _suppressPropertyChange = false;
        }

        /// <summary>
        /// 设置为支持属性变更通知
        /// </summary>
        public void SuppressNotifyPropertyChange()
        {
            _suppressPropertyChange = true;
        }

        /// <summary>
        /// 获取属性值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="property"></param>
        /// <returns></returns>
        protected T GetProperty<T>([CallerMemberName] string property = null)
        {
            return Properties.TryGetValue(property, out object obj) ?
                obj.ConvertTo(default(T), true) :
                default(T);
        }

        /// <summary>
        /// 设置属性值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="value"></param>
        /// <param name="property"></param>
        protected void SetProperty<T>(T value, [CallerMemberName] string property = null)
        {
            if (Equals(GetProperty<T>(property), value))
                return;
            OnPropertyChanging(property);
            if (value == null)
                Properties.Remove(property);
            else
                Properties[property] = value;
            OnPropertyChanged(property);
        }

        /// <summary>
        /// 扩展属性变更后事件
        /// </summary>
        /// <param name="property"></param>
        protected override void OnExtendedPropertyChanged(string property)
        {
            this.OnPropertyChanged("Item[]");
        }

        /// <summary>
        /// 扩展属性变更前事件
        /// </summary>
        /// <param name="property"></param>
        /// <param name="value"></param>
        protected override void OnExtendedPropertyChanging(string property, object value)
        {
            this.OnPropertyChanging("Item[]");
        }
    }
}